#pragma once
#include<iostream>
#include<string.h>
#include<unistd.h>
#include<sys/stat.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>

using namespace std;

enum 
{
    SocketError=2,BindError,ListenError
};

const int backlog=10;

class Sock
{
public:
    Sock()
    {}
    ~Sock()
    {}

    void Socket()
    {
        //创建套接字
        _sockfd=socket(AF_INET,SOCK_STREAM,0);
        if(_sockfd<0)
        {
            cout<<"create socket error,errno:"<<errno<<",strerror:"<<strerror<<endl;
            exit(SocketError);
        }
    }
    void Bind(uint16_t port)
    {
        //绑定套接字
        struct sockaddr_in local;//函数栈帧内的一个局部sock类型的对象
        memset(&local,0,sizeof(local));

        local.sin_family=AF_INET;
        local.sin_port=htons(port);
        local.sin_addr.s_addr=INADDR_ANY;

        if(bind(_sockfd,(struct sockaddr*)&local,sizeof(local))<0)//将数据填充到内核中的tcp_sock对象中
        {
            cout<<"bind socket error,errno:"<<errno<<",strerror:"<<strerror<<endl;
            exit(BindError);
        }
    }
    void Listen()
    {
        //监听套接字
        if(listen(_sockfd,backlog)<0)//listen的主要作用：将服务端套接字变成被动监听状态，使其能够收到来自其它套接字的连接请求
        {
            cout<<"listen socket error,errno:"<<errno<<",strerror:"<<strerror<<endl;
            exit(ListenError);
        }
    }
    int Accept(string& client_ip,uint16_t& client_port)
    {
        struct sockaddr_in client;//存放来自对方的套接字信息
        socklen_t len=sizeof(client);
        int rcv_fd=accept(_sockfd,(struct sockaddr*)&client,&len);//三次握手成功后，OS会把新建立的链接放到“全链接队列”，accept会将“全链接队列”中的链接拿到上层，把报文中发送方的套接字信息存储下来，并返回该报文所在的接受缓冲区
        if(rcv_fd<0)
        {
            cout<<"accept socket error,errno:"<<errno<<",strerror:"<<strerror<<endl;
            exit(ListenError);
        }
        char buf[64];
        inet_ntop(AF_INET,&client.sin_addr,buf,sizeof(buf));//将对方的ip信息由"网络字节序转"为"主机字节序"，并将转换后的结果存放到buf中
        client_ip=buf;
        client_port=ntohs(client.sin_port);
        
        return rcv_fd;
    }
    bool Connect(string& server_ip,uint16_t server_port)
    {
        struct sockaddr_in local;
        memset(&local,0,sizeof(local));

        local.sin_family=AF_INET;
        inet_pton(AF_INET,server_ip.c_str(),(struct sockaddr*)&local.sin_addr);
        local.sin_port=htons(server_port);

        int n=connect(_sockfd,(const struct sockaddr*)&local,sizeof(local));//向指定主机的指定端口号发送连接请求
        if(n<0)
        {
            cerr<<"connect to"<<server_ip<<":"<<server_port<<"fail..."<<endl;
            return false;
        }
        return true;
    }

    void Close()
    {
        close(_sockfd);
    }
public:
    int _sockfd;
};
